

#include "GameModel.h"
#include "stdlib.h"
#include <sstream>
#include <string>

using namespace std;

GameModel::GameModel(int size)
{
    //ctor
    if(size<1) size=1;
    _size=size;
    dataSize=_size*_size;
    data=new int[dataSize];
    for(int i=0; i<dataSize; i++)
    {
        data[i]=i;
    }
    //init space location
    spaceRow=spaceCol=0;
}

///@brief Get the block id with specific row and col.
///return >0 if the block exists, or -1 when error.
int GameModel::getData(int row,int col){
    if(row<0 || row>=_size || col<0 || col>=_size){
        return -1;
    }else{
        return data[row*_size+col];
    }
}

void GameModel::getSpaceLoc(int& row,int& col){
    row=spaceRow;
    col=spaceCol;
}

void GameModel::getDataLoc(int& row,int& col,int val){
    if(val<0 || val>=dataSize){
        row=col=-1;
    }else{
        for(int i=0;i<dataSize;i++){
            if(data[i]==val){
                row=i/_size;
                col=i%_size;
                return;
            }
        }
    }
}

bool GameModel::judgeByRowCol(int row,int col){
    if(row<0 || row>=_size || col<0 || col>=_size){
        return false;
    }else{
        int dr=spaceRow-row,dc=spaceCol-col;
        if((0==dr && (-1==dc || 1==dc)) || (0==dc && (-1==dr || 1==dr))){
            return true;
        }
        return false;
    }
}

bool GameModel::moveByRowCol(int row,int col){
    if(row<0 || row>=_size || col<0 || col>=_size){
        return false;
    }else{
        int dr=spaceRow-row,dc=spaceCol-col;
        if((0==dr && (-1==dc || 1==dc)) || (0==dc && (-1==dr || 1==dr))){
            //swap two data
            std::swap(data[spaceRow*_size+spaceCol],data[row*_size+col]);
            spaceRow=row;
            spaceCol=col;
            return true;
        }
        return false;
    }
}

bool GameModel::loadData(int dd[]){
    //check validity of the data
    int sum=0;
    for(int i=0;i<dataSize;i++){
        sum+=dd[i];
    }
    if(sum!=((dataSize-1)*dataSize/2)){
        return false;
    }
    //load
    for(int i=0;i<dataSize;i++){
        data[i]=dd[i];
        if(0==dd[i]){
            spaceRow=i/_size;
            spaceCol=i%_size;
        }
    }
    return true;
}

bool GameModel::moveByDirection(int d)
{
    //
    if(LEFT==d && 0<spaceCol){
        std::swap(data[spaceRow*_size+spaceCol],data[spaceRow*_size+spaceCol-1]);
        spaceCol--;
        return true;
    }
    else if(RIGHT==d && (_size-1)>spaceCol){
        std::swap(data[spaceRow*_size+spaceCol],data[spaceRow*_size+spaceCol+1]);
        spaceCol++;
        return true;
    }
    else if(UP==d && 0<spaceRow){
        std::swap(data[spaceRow*_size+spaceCol],data[(spaceRow-1)*_size+spaceCol]);
        spaceRow--;
        return true;
    }
    else if(DOWN==d && (_size-1)>spaceRow){
        std::swap(data[spaceRow*_size+spaceCol],data[(spaceRow+1)*_size+spaceCol]);
        spaceRow++;
        return true;
    }
    //
    return false;
}


void GameModel::random(const int nStep){
    const int VC=UP+DOWN;
    const int ALL_D[]={LEFT,RIGHT,UP,DOWN};
    //
    srand(unsigned(time(0)));
    int i=0,vi,v,vlast=LEFT;
    while(i<nStep){
        //random direction.
        vi=(int)rand()%4;
        v=ALL_D[vi];
        //Do not go back
        if(VC==(v+vlast)){
            continue;
        }
        //move
        if(moveByDirection(v)){
            vlast=v;
            i++;
        }
    }
}

int GameModel::getSize()
{
    return _size;
}


bool GameModel::judgeWin()
{
    //
    for(int i=0;i<dataSize;i++){
        if(data[i]!=i){
            return false;
        }
    }
    return true;
}


inline void GameModel::swap(int id1,int id2)
{
    int tmp=data[id1];
    data[id1]=data[id2];
    data[id2]=tmp;
}

GameModel::~GameModel()
{
    //dtor
    delete []data;
}

string GameModel::pullData(){
    std::stringstream ss;
    for(int i=0;i<dataSize;i++){
        if(i>0){ ss<<" ";}
        ss<<data[i];
    }
    return ss.str();
}

bool GameModel::pushData(string str){
    std::stringstream ss(str);
    int dd[dataSize];
    for(int i=0;i<dataSize;i++){
        //
        ss>>dd[i];
    }
    return loadData(dd);
}

string GameModel::toString(){
    std::stringstream ss;
    //
    for(int i=0;i<_size;i++){
        if(i>0) {
            ss<<"\n,{";
        }
        for(int j=0;j<_size;j++){
            if(j>0){
                ss<<",";
            }
            ss<<data[i*_size+j];
        }
        ss<<"}";
    }
    return ss.str();
}
